﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
using Szamlazz.Data;
using Szamlazz.Utils;
using R = Szamlazz.Data.Request;
using S = Szamlazz.Data.Response;
 
namespace Szamlazz.Integration
{
	/// <summary>
	/// A számla kérelmek megválaszolását végző osztály
	/// </summary>
	internal class ResponseManager : IDisposable
	{
		/// <summary>
		/// A válaszok tárolója
		/// </summary>
		private Dictionary<Guid, Invoice> Data { get; set; }
		/// <summary>
		/// Kérelmet intéző számla tároló
		/// </summary>
		internal InvoiceStore Store { get; private set; }
		/// <summary>
		/// Süti tároló
		/// </summary>
		private CookieContainer Cookies { get; set; }
		/// <summary>
		/// Web kliens
		/// </summary>
		private HttpClient Client { get; set; }
		/// <summary>
		/// Konstruktor, tároló alapján
		/// </summary>
		/// <param name="store">A tároló, amihez csatlakozni kell</param>
		internal ResponseManager(InvoiceStore store)
		{
			this.Store = store;
			this.Data = new Dictionary<Guid, Invoice>();
			this.Cookies = new CookieContainer();
		}

		/// <summary>
		/// Létrehozza az összes kérelemhez tartozó választ
		/// </summary>
		/// <returns></returns>
		internal IEnumerable<Invoice> CreateResponses()
		{
			foreach (var request in this.Store.Data)
			{
				yield return CreateResponse(request);
			}
		}

		internal Invoice CreateResponse(KeyValuePair<Guid, InvoiceRequest> request)
		{
			if (this.Data.ContainsKey(request.Key))
			{
				if (this.Data[request.Key].IsSuccessfull == false)
				{
					this.Data[request.Key] = CreateResponse(request.Value);
				}

				return this.Data[request.Key];
			}
			else
			{
				var ret = CreateResponse(request.Value);

				this.Data.Add(request.Key, ret);

				return ret;
			}
		}

		/// <summary>
		/// Létrehozza a választ az adott kérelemhez
		/// </summary>
		/// <param name="invoice">A kérelem</param>
		/// <returns>A létrejövő számla objektum</returns>
		private Invoice CreateResponse(InvoiceRequest invoice)
		{
			var ret = new Invoice(invoice);
			var validation = invoice.Validate();

			if (validation.IsSuccessfull)
			{
				var data = ret.Request.GenerateForRequest(this.Store.Authentication, this.Store.Provider);

				if (this.Client == null)
				{
					this.Cookies = new CookieContainer();
					var handler = new HttpClientHandler { CookieContainer = this.Cookies, UseCookies = true, AllowAutoRedirect = false };

					this.Client = new HttpClient(handler);
				}

				try
				{
					ret.FromResponse(SendRequest(data), this.Store.Authentication.IsESigned);
				}
				catch (Exception ex)
				{
					ret.FromException(ex);
				}
			}
			else
			{
				ret.FromValidation(validation);
			}

			return ret;
		}

		/// <summary>
		/// Elküldi a számla kérelmet a csatornán
		/// </summary>
		/// <param name="data">XML kommunikációs adat</param>
		/// <returns>XML válasz adat</returns>
		private S.szamlavalasz SendRequest(R.xmlszamla data)
		{
			var requestSerializer = new XmlSerializer(typeof(R.xmlszamla));
			var sb = new StringBuilder();
			var xw = XmlWriter.Create(sb, new XmlWriterSettings
			{
				Encoding = Encoding.UTF8,
				Indent = true,
				OmitXmlDeclaration = true,
			});
			requestSerializer.Serialize(xw, data);
			var bytes = Encoding.UTF8.GetBytes(sb.ToString());

			var content = new ByteArrayContent(bytes);
			var text = content.ReadAsStringAsync().Result;

			using (var form = new MultipartFormDataContent())
			{
				form.Add(content, "action-xmlagentxmlfile", "data.xml");

				var result = this.Client.PostAsync("https://www.szamlazz.hu/szamla/", form).Result;

				if (result.IsSuccessStatusCode)
				{
					var response = result.Content.ReadAsStringAsync().Result;

					try
					{
						var responseSerializer = new XmlSerializer(typeof(S.szamlavalasz));
						var xr = XmlReader.Create(new StringReader(response));

						return responseSerializer.Deserialize(xr) as S.szamlavalasz;
					}
					catch (Exception ex)
					{
						throw new InvalidOperationException(string.Format("A számla válasz visszaolvasása sikertelen: {0}, {1}", text, response), ex);
					}
				}
				else
				{
					throw new InvalidOperationException(string.Format("A szamlazz.hu hívásakor hiba történt: HTTP {0}", result.StatusCode));
				}
			}

			throw new InvalidOperationException(string.Format("A számla létrehozása sikertelen: {0}", text));
		}

		public void Dispose()
		{
			if (this.Cookies != null)
			{
				this.Cookies = null;
			}
		}
	}
}
